#!/usr/bin/env node

const HELP = `
USAGE
  zed-local  [options]  [zed args]

SUMMARY
  Runs 1-6 instances of Zed using a locally-running collaboration server.
  Each instance of Zed will be signed in as a different user specified in
  either \`.admins.json\` or \`.admins.default.json\`.

  All arguments after the initial options will be passed through to the first
  instance of Zed. This can be used to test SSH remoting along with collab, like
  so:

  $ script/zed-local -2 ssh://your-ssh-uri-here

OPTIONS
  --help            Print this help message
  --release         Build Zed in release mode
  -2, -3, -4, ...   Spawn multiple Zed instances, with their windows tiled.
  --top             Arrange the Zed windows so they take up the top half of the screen.
  --stable          Use stable Zed release installed on local machine for all instances (except for the first one).
  --preview         Like --stable, but uses the locally-installed preview release instead.
`.trim();

const { spawn, execSync, execFileSync } = require("child_process");
const assert = require("assert");

let users;
if (process.env.SEED_PATH) {
  users = require(process.env.SEED_PATH).admins;
} else {
  users = require("../crates/collab/seed.default.json").admins;
  try {
    const defaultUsers = users;
    const customUsers = require("../crates/collab/seed.json").admins;
    assert(customUsers.length > 0);
    users = customUsers.concat(
      defaultUsers.filter((user) => !customUsers.includes(user)),
    );
  } catch (_) {}
}

const RESOLUTION_REGEX = /(\d+) x (\d+)/;
const DIGIT_FLAG_REGEX = /^--?(\d+)$/;

let instanceCount = 1;
let isReleaseMode = false;
let isTop = false;
let othersOnStable = false;
let othersOnPreview = false;
let isStateful = false;

const args = process.argv.slice(2);
while (args.length > 0) {
  const arg = args[0];

  const digitMatch = arg.match(DIGIT_FLAG_REGEX);
  if (digitMatch) {
    instanceCount = parseInt(digitMatch[1]);
  } else if (arg === "--release") {
    isReleaseMode = true;
  } else if (arg == "--stateful") {
    isStateful = true;
  } else if (arg === "--top") {
    isTop = true;
  } else if (arg === "--help") {
    console.log(HELP);
    process.exit(0);
  } else if (arg === "--stable") {
    othersOnStable = true;
  } else if (arg === "--preview") {
    othersOnPreview = true;
  } else {
    break;
  }

  args.shift();
}
const os = require("os");
const platform = os.platform();

let screenWidth, screenHeight;
const titleBarHeight = 24;

if (platform === "darwin") {
  // macOS
  const displayInfo = JSON.parse(
    execFileSync("system_profiler", ["SPDisplaysDataType", "-json"], {
      encoding: "utf8",
    }),
  );
  const mainDisplayResolution = displayInfo?.SPDisplaysDataType?.flatMap(
    (display) => display?.spdisplays_ndrvs,
  )
    ?.find((entry) => entry?.spdisplays_main === "spdisplays_yes")
    ?._spdisplays_resolution?.match(RESOLUTION_REGEX);
  if (!mainDisplayResolution) {
    throw new Error("Could not parse screen resolution");
  }
  screenWidth = parseInt(mainDisplayResolution[1]);
  screenHeight = parseInt(mainDisplayResolution[2]) - titleBarHeight;
} else if (platform === "linux") {
  // Linux
  try {
    const xrandrOutput = execSync('xrandr | grep "\\*" | cut -d" " -f4', {
      encoding: "utf8",
    }).trim();
    [screenWidth, screenHeight] = xrandrOutput.split("x").map(Number);
  } catch (err) {
    console.log(err);
    throw new Error("Could not get screen resolution");
  }
} else if (platform === "win32") {
  // windows
  try {
    const resolutionOutput = execSync(
      `powershell -Command "& {Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Size}"`,
      { encoding: "utf8" },
    ).trim();
    [screenWidth, screenHeight] = resolutionOutput.match(/\d+/g).map(Number);
  } catch (err) {
    console.log(err);
    throw new Error("Could not get screen resolution on Windows");
  }
}

if (platform !== "win32") {
  screenHeight -= titleBarHeight;
}

if (isTop) {
  screenHeight = Math.floor(screenHeight / 2);
}

// Determine the window size for each instance
let rows;
let columns;
switch (instanceCount) {
  case 1:
    [rows, columns] = [1, 1];
    break;
  case 2:
    [rows, columns] = [1, 2];
    break;
  case 3:
  case 4:
    [rows, columns] = [2, 2];
    break;
  case 5:
  case 6:
    [rows, columns] = [2, 3];
    break;
}

const instanceWidth = Math.floor(screenWidth / columns);
const instanceHeight = Math.floor(screenHeight / rows);

// If a user is specified, make sure it's first in the list
const user = process.env.ZED_IMPERSONATE;
if (user) {
  users = [user].concat(users.filter((u) => u !== user));
}

let buildArgs = ["build"];
let zedBinary = "target/debug/zed";
if (isReleaseMode) {
  buildArgs.push("--release");
  zedBinary = "target/release/zed";
}

try {
  execFileSync("cargo", buildArgs, {
    stdio: "inherit",
  });
} catch (e) {
  process.exit(0);
}

setTimeout(() => {
  for (let i = 0; i < instanceCount; i++) {
    const row = Math.floor(i / columns);
    const column = i % columns;
    let position;
    if (platform == "win32") {
      position = [column * instanceWidth, row * instanceHeight].join(",");
    } else {
      position = [
        column * instanceWidth,
        row * instanceHeight + titleBarHeight,
      ].join(",");
    }
    const size = [instanceWidth, instanceHeight].join(",");
    let binaryPath = zedBinary;
    if (i != 0 && othersOnStable) {
      binaryPath = "/Applications/Zed.app/Contents/MacOS/zed";
    } else if (i != 0 && othersOnPreview) {
      binaryPath = "/Applications/Zed Preview.app/Contents/MacOS/zed";
    }
    spawn(binaryPath, i == 0 ? args : [], {
      stdio: "inherit",
      env: Object.assign({}, process.env, {
        ZED_IMPERSONATE: users[i],
        ZED_WINDOW_POSITION: position,
        ZED_STATELESS: isStateful && i == 0 ? "" : "1",
        ZED_ALWAYS_ACTIVE: "1",
        ZED_SERVER_URL: "http://localhost:3000",
        ZED_RPC_URL: "http://localhost:8080/rpc",
        ZED_ADMIN_API_TOKEN: "internal-api-key-secret",
        ZED_WINDOW_SIZE: size,
        ZED_CLIENT_CHECKSUM_SEED: "development-checksum-seed",
        RUST_LOG: process.env.RUST_LOG || "info",
      }),
    });
  }
}, 0.1);
